/**
 *  Copyright (C) 2023 - Fake Inc.
 *
 *  All Rights Reserved.

*/
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

namespace Fake {

class DataBuffer {
 public:
  void push_data(int32_t p_data) noexcept {
    const std::lock_guard<std::mutex> lck_mux{mut_data_};
    data_queue_.push(p_data);
    data_available_.notify_one();
  }

  int32_t pop_data() noexcept {
    std::unique_lock<std::mutex> lck_mux{mut_data_};
    data_available_.wait(lck_mux, [&] { return !data_queue_.empty(); });
    int32_t front_data{0};
    front_data = data_queue_.front();
    data_queue_.pop();
    return front_data;
  }

 private:
  std::queue<int32_t> data_queue_;
  std::mutex mut_data_;
  std::condition_variable data_available_;
};

static void process_producer_s(DataBuffer& buf_out) noexcept {
  int32_t proidx{0};
  for (; proidx < 10; ++proidx) {
    buf_out.push_data(proidx);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
}

static void process_consumer_s(DataBuffer& buf_in) noexcept {
  int32_t conidx{0};
  for (; conidx < 10; ++conidx) {
    int32_t p_data{0};
    p_data = buf_in.pop_data();
    std::cout << "Consumer received: " << p_data << std::endl;
  }
}

}  // namespace Fake

int32_t main() {
  int32_t main_res{0};

  try {
    Fake::DataBuffer buf_in;

    std::thread producer{Fake::process_producer_s, std::ref(buf_in)};
    std::thread consumer{Fake::process_consumer_s, std::ref(buf_in)};

    producer.join();
    consumer.join();

    main_res = 0;
  } catch (...) {
    std::cerr << "Exception occurred" << std::endl;
    main_res = 1;  // Exception occurred
  }

  return main_res;
}
